package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.message.NewsAutoScanConstants;
import com.heima.common.constants.message.WmNewsMessageConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.threadlocal.WmThreadLocalUtils;
import com.heima.model.wemedia.dtos.NewsAuthDto;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.model.wemedia.vo.WmNewsVo;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmNewsService;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.record.DVALRecord;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Jt
 * @date 2021/4/15 17:14
 */
@Service
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Value("${file.oss.web-site}")
    String webSite;

    @Resource
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Resource
    private WmMaterialMapper wmMaterialMapper;

    @Resource
    private KafkaTemplate kafkaTemplate;

    @Resource
    private WmNewsMapper wmNewsMapper;

    @Resource
    private WmUserMapper wmUserMapper;

    /**
     * 查询所有文章列表
     * @param wmNewsPageReqDto
     * @return
     */
    @Override
    public ResponseResult findAll(WmNewsPageReqDto wmNewsPageReqDto) {
//        1. 检查参数
        if (wmNewsPageReqDto == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
        wmNewsPageReqDto.checkParam(); // 检查分页
//        2. 查询条件封装
        LambdaQueryWrapper<WmNews> lambdaQuery = Wrappers.lambdaQuery();
//        3. 根据状态查询
        if (wmNewsPageReqDto.getStatus() != null) {
            lambdaQuery.eq(WmNews::getStatus,wmNewsPageReqDto.getStatus());
        }
//        4. 关键字查询
        if (StringUtils.isNotBlank(wmNewsPageReqDto.getKeyword())) {
            lambdaQuery.like(WmNews::getTitle,wmNewsPageReqDto.getKeyword());
        }
//        5. 频道查询
        if (wmNewsPageReqDto.getChannelId() != null) {
            lambdaQuery.eq(WmNews::getChannelId,wmNewsPageReqDto.getChannelId());
        }
//        6. 发布时间查询
        if (wmNewsPageReqDto.getBeginPubDate() != null && wmNewsPageReqDto.getEndPubDate() != null) {
            // 开始时间和结束时间
            lambdaQuery.between(WmNews::getPublishTime,wmNewsPageReqDto.getBeginPubDate(),wmNewsPageReqDto.getEndPubDate());
        }
//        7. 根据当前用户登录的ID查询条件
        Integer userId = WmThreadLocalUtils.getUser().getId();
        if (userId != null) {
            lambdaQuery.eq(WmNews::getUserId,userId);
        }
//        8. 分页条件
        Page<WmNews> newsPage = new Page<>(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize());
//        9. 根据文章创建时间进行降序排序
        lambdaQuery.orderByDesc(WmNews::getCreatedTime);
        IPage<WmNews> page = page(newsPage, lambdaQuery);
//        10. 返回结果 (添加图片访问的前缀)
        PageResponseResult result = new PageResponseResult(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize(), (int) page.getTotal());
        result.setData(page.getRecords());
        result.setHost(webSite);
        return result; //返回结果
    }

    /**
     * 保存或修改 自媒体文章
     * @param wmNewsDto 文章数据
     * @param isSubmit 是否提交 1->提交  0->草稿
     * @return
     */
    @Override
    public ResponseResult submitNews(WmNewsDto wmNewsDto, Short isSubmit) {
//        1. 检查参数 封装成一个wmNews对象
        if (wmNewsDto == null || StringUtils.isBlank(wmNewsDto.getContent())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"文章的内容不能为空!");
        }
//        判断是否登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null || user.getId() == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"您还未登录,请登录之后再进行操作!");
        }
//        封装成wmNews
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto,wmNews);
//        如果文章布局为自动,那么设置为null
        if (wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            wmNews.setType(null);
        }
//        封面图片是为List<String> 集合 转成字符串 多个图片 使用逗号(,)拼接
        List<String> images = wmNewsDto.getImages();
        if (images != null && !images.isEmpty()) {
            String collect = images.stream() // 获取流
                    .map((url) -> url.replace(webSite, "")) // 去除前缀
                    .collect(Collectors.joining(",")); // 终止操作,拼接字符串(集合转成字符串)
            wmNews.setImages(collect); // 存入wmNews中
        }
        wmNews.setUserId(user.getId()); // 设置当前用户的ID
//        2.  保存或修改wmNews
        saveOrUpdateWmNews(wmNews);
//        抽取文章里面所有保存的图片路径
        List<String> materialList = parseContentImage(wmNewsDto.getContent());
//        3.  判断是提交还是草稿 如果提交要保存内容和素材的关联关系
        if (materialList != null && !materialList.isEmpty()) {
            saveForContent (materialList,wmNews.getId(),WemediaConstants.WM_CONTENT_REFERENCE);
        }
//        4.  判断是提交还是草稿 如果提交要保存封面和素材的关联关系
        saveRealtiveForCover (materialList,wmNews,wmNewsDto);
        // 4 发消息给admin审核文章
        Map map = new HashMap();
        map.put("wmNewsId", wmNews.getId());
        kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, JSON.toJSONString(map));
        return ResponseResult.okResult();
    }


    /**
     * 保存封面与素材的关联关系
     * @param materialList 图片url列表
     * @param wmNews
     * @param wmNewsDto
     */
    private void saveRealtiveForCover(List<String> materialList, WmNews wmNews, WmNewsDto wmNewsDto) {
        List<String> images = wmNewsDto.getImages(); // 选择的图片
//        说明是选择的自动
        if ((wmNewsDto.getType()).equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            int size = materialList.size();
//            进行判断
            if (size > 0 && size <= 2) {
//                那么说明就是单图
                 images = materialList.stream().limit(1).collect(Collectors.toList());
                 wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
            } else if (size > 2) {
//                说明就是多图
                images = materialList.stream().limit(3).collect(Collectors.toList());
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
            } else {
//                说明是无图
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            if (images != null && images.size() > 0) {
//                将图片集合转为字符串
                wmNews.setImages(imageListToStr(images,webSite));
            }
//            TODO
            updateById(wmNews);
        }
//        保存图片列表和素材关系
        if (images != null && images.size() > 0) {
             images = images.stream().map(map -> map.replace(webSite, "").replace(" ", ""))
                    .collect(Collectors.toList());
//             保存关联关系
             saveRelative(images,wmNews.getId(),WemediaConstants.WM_IMAGE_REFERENCE); // 封面引用
        }
    }

    private void saveForContent(List<String> materialList, Integer newId, Short wmContentReference) {
        saveRelative(materialList,newId,wmContentReference);
    }



    /**
     * 保存关联关系
     * @param materialList 素材的图片路径
     * @param newId 文章的ID
     * @param type 引用类型 0代表内容引用 1代表封面引用
     */
    private void saveRelative(List<String> materialList, Integer newId, Short type) {
//        1. 根据图片列表和登录用户id获取素材列表
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(
                Wrappers.<WmMaterial>lambdaQuery()
                        .in(WmMaterial::getUrl, materialList)
                        .eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId())
        );
            if (wmMaterials.isEmpty()) { // 进行判断,如果为空,那么就抛异常
                CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"获取素材信息失败");
            }
//            2. 判断素材是否存在 TODO
        Map<String, Integer> idAndUrl = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (String materialUrl : materialList) {
            if (ObjectUtils.isEmpty(idAndUrl.get(materialUrl))) {
                CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"获取不到素材数据");
            }
            arrayList.add(idAndUrl.get(materialUrl));
        }
//        3. 得到素材id 文章id 引用的类型
        wmNewsMaterialMapper.saveRelations(arrayList,newId,type);
    }

    /**
     * 抽取文章中所有包含图片的路径
     * @param content
     * @return
     */
    private List<String> parseContentImage(String content) {
//        将json字符串转为map集合
        List<String> materialList = new ArrayList<>();
        List<Map> list = JSONArray.parseArray(content, Map.class);
        return list.stream()
                .filter(map -> map.get("type").equals(WemediaConstants.WM_NEWS_TYPE_IMAGE)) // 过滤掉是图片
                .map(map -> (String)map.get("value")) // 通过映射处理,获取图片的Url
                .map(imageUrl -> imageUrl.replace(webSite,"")) // 去除前缀
                .collect(Collectors.toList()); // 将图片收集为list集合
    }

    /**
     * 保存或修改文章的方法
     * @param wmNews
     */
    private void saveOrUpdateWmNews(WmNews wmNews) {
        if (wmNews.getId() == null) {
//            说明为空,那就是添加
            wmNews.setCreatedTime(new Date());
            wmNews.setEnable((short)1); // 上架
            wmNews.setSubmitedTime(new Date()); // 提交的时间
            save(wmNews); // 保存
        } else {
//            说明有对应的数据,那就是修改
//            但是需要删除素材与文章的关联表中的数据
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
            .eq(WmNewsMaterial::getNewsId,wmNews.getId())
            );
            updateById(wmNews);
        }
    }


    /**
     * 图片列表转为字符串 , 并且去除图片的前缀
     * @param images 图片列表
     * @param webSite 图片前缀
     * @return
     */
    private String imageListToStr (List<String> images , String webSite) {
        return images.stream() // 获取流
                .map(url -> url.replace(webSite,"")) // 去除前缀
                .collect(Collectors.joining(",")); // 收集为字符串,并以逗号(,)分隔
    }


    /**
     * 根据id获取文章信息
     * @param id
     * @return
     */
    @Override
    public ResponseResult findWmNewsById(Integer id) {
//        1. 检查参数
        if (ObjectUtils.isEmpty(id)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
//        2. 执行查询
        WmNews wmNews = getById(id);
        if (ObjectUtils.isEmpty(wmNews)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
//        3. 返回结果
        ResponseResult result = ResponseResult.okResult(wmNews);
        result.setHost(webSite);
        return result;
    }

    /**
     * 根据id删除文章
     * @param id
     * @return
     */
    @Override
    public ResponseResult delNews(Integer id) {
//        1. 检查参数
        if (ObjectUtils.isEmpty(id)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
//        2. 根据id获取对应的数据,如为空抛异常
        WmNews wmNews = getById(id);
        if (ObjectUtils.isEmpty(wmNews)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"对应的文章不存在");
        }
//        3. 判断文章的状态是否为9且是否已经上架,下架的话可以删除,其他状态也可以删除
        if ((wmNews.getStatus()).equals(WmNews.Status.PUBLISHED.getCode()) &&
        wmNews.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"文章已经发布且上架,不能进行删除,请下架后在删除!");
        }
//        4. 去除素材与文章关联数据的数据
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
        .eq(WmNewsMaterial::getNewsId,wmNews.getId()));
//        5. 删除文章
        removeById(wmNews.getId());
//        6. 返回结果
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /**
     * 上下架
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
//        1. 检查参数
        if (ObjectUtils.isEmpty(dto) || ObjectUtils.isEmpty(dto.getId())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
//        2. 根据id去查询对应的文章数据,如果没有抛异常
        WmNews wmNews = getById(dto.getId());
        if (ObjectUtils.isEmpty(wmNews)) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"对应的文章数据不存在");
        }
//        3. 判断文章是否发布,如不是发布状态则不能上下架
        if (!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"文章不是发布状态,不能进行上下架");
        }
//        4. 修改文章的状态 TODO 同步到app端(后期做)
        if (dto.getEnable() != null && dto.getEnable() > -1 && dto.getEnable() < 2) {
            update(Wrappers.<WmNews>lambdaUpdate().eq(WmNews::getId,dto.getId()).set(
                    WmNews::getEnable,dto.getEnable()
            ));
        }
        if (wmNews.getArticleId() != null) {
            Map<String, Object> map = new HashMap<>();
            map.put("enable",dto.getEnable());
            map.put("articleId",wmNews.getArticleId());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(map));
        }
//        5. 返回结果
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /**
     * 查询需要发布的文章ID列表
     * @return
     */
    @Override
    public List<Integer> findRelease() {
//        查询状态为4或为8的数据且发布时间小于当前时间的数据
        List<WmNews> list = list(Wrappers.<WmNews>lambdaQuery()
                .le(WmNews::getPublishTime, new Date())
                .in(WmNews::getStatus, 4, 8)
                .select(WmNews::getId));
//        使用流转换
        List<Integer> integerList = list.stream()
                .map(WmNews::getId).collect(Collectors.toList());
        return integerList;
    }


    /**
     * 查询文章列表
     * @param dto
     * @return
     */
    @Override
    public ResponseResult findList(NewsAuthDto dto) {
//        检查参数
        if (ObjectUtils.isEmpty(dto)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
//        检查分页
        dto.checkParam();
//        记录当前页
        Integer currentPage = dto.getPage();
//        设置起始页
        dto.setPage((dto.getPage()-1) * dto.getSize());
        if (StringUtils.isNotBlank(dto.getTitle())) {
            dto.setTitle("%" + dto.getTitle() + "%");
        }
//        分页查询
        long listCount = wmNewsMapper.findListCount(dto);
        List<WmNewsVo> wmNewsVoList = wmNewsMapper.findListAndPage(dto);
//        统计有多少条的数据
//        封装结果返回
        PageResponseResult result = new PageResponseResult(currentPage, dto.getSize(), (int) listCount);
        result.setData(wmNewsVoList);
//        result.setHost(webSite);
        return result;
    }


    /**
     * 查询文章详情
     * @param id
     * @return
     */
    @Override
    public ResponseResult findWmNewsVo(Integer id) {
//        1. 校验参数
        if (id == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
//        2. 查询文章信息
        WmNews wmNews = getById(id);
        if (ObjectUtils.isEmpty(wmNews)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"对应的文章信息不存在");
        }
//        3. 查询作者
        if (wmNews.getUserId() == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"对应的作者不存在");
        }
        WmUser news = wmUserMapper.selectById(wmNews.getUserId());
//        4. 封装vo信息返回
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews,wmNewsVo);
        if (news != null) {
            wmNewsVo.setAuthorName(news.getName());
        }
        ResponseResult result = ResponseResult.okResult(wmNewsVo);
        result.setHost(webSite);
        return result;
    }


    /**
     * 自媒体文章人工审核
     * @param status  2  审核失败  4 审核成功
     * @param dto
     * @return
     */
    @Override
    public ResponseResult updateStatus(Short status, NewsAuthDto dto) {
//        1. 校验参数
        if (dto == null || dto.getId() == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID);
        }
//        2. 查询文章信息
        WmNews wmNews = getById(dto.getId());
        if (ObjectUtils.isEmpty(wmNews)) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"对应的文章信息不存在");
        }
//        3. 修改文章的状态
        wmNews.setStatus(status);
//        判断错误信息是否是不为空,如果是,那么就保存到文章的reason中
        if (StringUtils.isNotBlank(dto.getMsg())) {
            wmNews.setReason(dto.getMsg());
        }
        updateById(wmNews);
//        4. 返回结果
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

}
